#pragma once 
	
namespace Melting
{
	namespace Profiling
	{
	#if		ENABLE_PROFILING

	#define		PROFILING_INITIALIZE( _maxStats, _maxThreads )			Profiling::Initialize( (_maxStats), (_maxThreads) )
	#define		PROFILING_RELEASE()										Profiling::Release()

	#define		PROFILING_INITIALIZE_TLS()								Profiling::InitializeTLS()
	#define		PROFILING_RELEASE_TLS()									Profiling::ReleaseTLS()

	#define 	PROFILING_BEGINRECORDTIME( _id, _strName )				Profiling::StatsID _id = Profiling::BeginRecordTime( (_strName) )
	#define 	PROFILING_ENDRECORDTIME( _id )							Profiling::EndRecordTime( (_id) )

	#define 	PROFILING_MERGETLSSTATS()								Profiling::MergeTLSStats()
	#define 	PROFILING_SORTSTATS()									Profiling::SortStats() 

	#define		PROFILING_GETSTATS( _statsBuffer )						_statsBuffer = Profiling::GetStats()

	#define 	PROFILING_PRINTSTATS()									Profiling::PrintStats()
	#define 	PROFILING_CLEARSTATS()									Profiling::ClearStats()


	#else

	#define		PROFILING_INITIALIZE( _maxStats, _maxThreads )	
	#define		PROFILING_RELEASE()										

	#define		PROFILING_INITIALIZE_TLS()								
	#define		PROFILING_RELEASE_TLS()									

	#define 	PROFILING_BEGINRECORDTIME( _id, _strName )					
	#define 	PROFILING_ENDRECORDTIME( _id )							

	#define 	PROFILING_MERGETLSSTATS()								
	#define 	PROFILING_SORTSTATS()		

	#define		PROFILING_GETSTATS( _statsBuffer )									

	#define 	PROFILING_PRINTSTATS()									
	#define 	PROFILING_CLEARSTATS()	

	#endif

		/**
			How to use Profiling : 

			//-------------------------------------------------
			// In your [ main thread ], call once
			Profiling::Initialize( 10000, 8 );

			// In each [ sub-thread ], call once 
			Profiling::InitializeTLS(); 

			//-------------------------------------------------
			// When you want to record a time measure [ from any thread ]
			StatsID id = Profiling::BeginRecordTime( "Name of the Measure" );
			{
				// The code you want to profile
				// ...
			}
			Profiling::EndRecordTime( id );


			//-------------------------------------------------
			// When you want to process the stats (e.g. print) ,
			// or before leaving your sub-thread :
			//  first merge them [ from every thread (sub + main) ]
			Profiling::MergeTLSStats();

			//  then sort them , 
			//	and finally print them
			Profiling::SortStatsByStartTime();
			Profiling::PrintStats();

			// Clear them when you're done processing
			Profiling::ClearStats();

			
			//-------------------------------------------------
			// Before leaving any [ sub-thread ]
			Profiling::ReleaseTLS();

			// Before leaving the [ main thread ]
			Profiling::Release();

		*/

		/// Time stats 
		struct TimeStats
		{ 
			LARGE_INTEGER			mStartTime;
			LARGE_INTEGER			mEndTime;
			Parallel::ThreadID		mThreadID;
			const char*				mstrName;
		};
		
		/// Buffer in which we keep the Stats
		typedef std::vector< TimeStats >	StatsBuffer;
		
		typedef UINT32 StatsID;
	 

		/// Initialize the profiling system, and the main thread ThreadLocalStorage, bspecifying the max amount of stats recordable, and the max amount of threads -- call it from the main thread only
		void				Initialize		( UINT32 _iMaxStats, UINT32 _iMaxThreads );

		/// Release the profiling system , and the main thread ThreadLocalStorage -- call it from the main thread only
		void				Release			( void );

		/// Intialize ThreadLocalStorage -- call this function from every profiled thread
		void				InitializeTLS	( void );

		/// Dentialize ThreadLocalStorage -- call this function from every profiled thread
		void				ReleaseTLS		( void );

		/// Merge the stats from the ThreadLocalStorage with the global stats, and clears the TLS - call it from every profiled thread
		void				MergeTLSStats	( void );

		/// Start measuring a time -- end the measuring with EndRecordTime(...)
		StatsID 			BeginRecordTime	( const char* _strName );

		/// End the measuring started with _iStartID = BeginRecordTime(...)
		void				EndRecordTime	( StatsID _iStatID );

		/// Get the system start time
		LARGE_INTEGER		GetSystemStartTime	( void );
		/// Get the system frequency
		LARGE_INTEGER		GetSystemFrequency	( void );

		/// Get the buffer of statistics
		const StatsBuffer&	GetStats		( void );

		/// Clears the the stats
		void				ClearStats		( void );
		

		/// Sort the stats by time
		void				SortStatsByStartTime( void );

		/// Sort the stats by thread
		void				SortStatsByThread( void );

		/// Print some stats
		void				PrintStats		( void );

	}// namespace Parallel

}// namespace Melting